﻿using System;
using System.ComponentModel;
using Finance.Annotations;
using Finance.Data;
using Finance.Data.Entities;

namespace Finance.ViewModels
{
	internal class DayDocumentsViewModel : INotifyPropertyChanged
	{
		private readonly IFinanceDataBase _dataBase;
		private readonly Action<Exception> _exceptionHandler;
		private readonly Func<Note, bool> _editBoolAction;
		private readonly Func<IDocumentBase, bool> _editDocumentAction;
		private DateTime _selectedDate;
		public event PropertyChangedEventHandler PropertyChanged;

		public DayDocumentsViewModel(IFinanceDataBase dataBase,
			Action<Exception> exceptionHandler,
			Func<Note,bool> editBoolAction,
			Func<IDocumentBase, bool> editDocumentAction)
		{
			_dataBase = dataBase;
			_exceptionHandler = exceptionHandler;
			_editBoolAction = editBoolAction;
			_editDocumentAction = editDocumentAction;

			Items = new BindingList<object>();

			SelectedDate = DateTime.Now;
		}

		public DateTime SelectedDate
		{
			get { return _selectedDate; }
			set
			{
				if (value.Equals(_selectedDate)) return;
				_selectedDate = value;
				UpdateItems();
				OnPropertyChanged("SelectedDate");
			}
		}

		/// <summary>
		/// Documents and notes
		/// </summary>
		public BindingList<object> Items { get; private set; }

		void UpdateItems()
		{
			if (_dataBase == null)
				return;

			try
			{
				Items.Clear();

				foreach (var note in _dataBase.Notes.RetrieveNotes(_selectedDate))
				{
					Items.Add(note);
				}

				foreach (var document in _dataBase.Documents.RetrieveDocuments(_selectedDate))
				{
					Items.Add(document);
				}

				foreach (var debtDocument in _dataBase.DebtDocuments.RetrieveDocuments(_selectedDate))
				{
					Items.Add(debtDocument);
				}

				foreach (var repayDebtDocument in _dataBase.RepayDebtDocuments.RetrieveDocuments(_selectedDate))
				{
					Items.Add(repayDebtDocument);
				}
			}
			catch (Exception exception)
			{
				if (_exceptionHandler != null)
					_exceptionHandler(exception);
			}
		}

		public void RemoveItem(object item)
		{
			if (item == null)
				return;

			var document = item as Document;
			if (document != null)
			{
				_dataBase.Documents.Delete(document);
			}
			else if (item is DebtDocument)
			{
				_dataBase.DebtDocuments.Delete((DebtDocument)item);
			}
			else if (item is RepayDebtDocument)
			{
				_dataBase.RepayDebtDocuments.Delete((RepayDebtDocument)item);
			}
			else
			{
				var note = item as Note;
				if (note != null)
					_dataBase.Notes.Delete(note);
			}

			if(Items.Contains(item))
				Items.Remove(item);
		}

		public void EditItem(object item)
		{
			if (item == null)
				return;

			var selectedDocument = item as IDocumentBase;
			if (selectedDocument != null)
				EditDocument(selectedDocument);

			var selectedNote = item as Note;
			if (selectedNote != null)
				EditNote(selectedNote);

			UpdateItems();
		}

		private bool EditNote(Note note)
		{
			if (_editBoolAction == null || !_editBoolAction(note))
				return false;

			try
			{
				if (!string.IsNullOrEmpty(note.Text))
				{
					note.Save();
					return true;
				}
				//Remove empty note
				if (note.Id != Guid.Empty)
				{
					_dataBase.Notes.Delete(note);
					if (Items.Contains(note))
						Items.Remove(note);
				}
			}
			catch (Exception exception)
			{
				_exceptionHandler(exception);
			}
			return false;
		}

		private bool EditDocument(IDocumentBase document)
		{
			if (_editDocumentAction == null || !_editDocumentAction(document))
				return false;

			try
			{
				document.Save();
				return true;
			}
			catch (Exception exception)
			{
				if (_exceptionHandler != null)
					_exceptionHandler(exception);
			}
			return false;
		}

		private IDocumentBase CreateDocument(DocumentType documentType)
		{
			switch (documentType)
			{
				case DocumentType.Dohod:
				case DocumentType.Rashod:
					return _dataBase.Documents.NewItem();
				case DocumentType.DohodZaim:
				case DocumentType.RashodZaim:
					return _dataBase.DebtDocuments.NewItem();
				case DocumentType.DohodZaimBack:
				case DocumentType.RashodZaimBack:
					return _dataBase.RepayDebtDocuments.NewItem();
				default:
					throw new Exception("Unkonw document type");
			}
		}

		public void AddDocument(DocumentType documentType)
		{
			var newDocument = CreateDocument(documentType);
            newDocument.DocumentType = documentType;
			newDocument.DocDate = SelectedDate.Date;

			if (EditDocument(newDocument))
			{
				if (newDocument.DocDate.Date == SelectedDate.Date)
				{
					Items.Add(newDocument);
				}
			}
		}
			
		[NotifyPropertyChangedInvocator]
		protected virtual void OnPropertyChanged(string propertyName)
		{
			var handler = PropertyChanged;
			if (PropertyChanged != null) handler(this, new PropertyChangedEventArgs(propertyName));
		}

		public void NewNote()
		{
			var note = _dataBase.Notes.NewItem();
			note.DateTime = SelectedDate.Date;
			if (EditNote(note))
			{
				Items.Add(note);
			}
		}
	}
}
